iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
Modern Web

Laravel 那麼好用還需要自幹框架嗎系列 第 13

Day 13:DatabaseServiceProvider 建立資料庫連線

  • 分享至 

  • xImage
  •  

看了怎麼建立 migration 檔案之後,今天我們來看 Laravel 怎麼建立和資料庫的連線。

如各位所知道的,Laravel 有許多資料庫的選擇,我們很難在短時間內看完所有連線資料庫的方式。所以我們先選定目標:看 Laravel 對 MySQL 的連線方式是怎麼建立的。選定目標之後再往下追,所需要看的方向就會比較明確了。

首先,根據我們對 Laravel 的理解,我們知道資料庫連線相關的內容應該是在 Illuminate\Database 裡面,所以對應的資料夾是 laravel/framework/src/Illuminate/Database

我們看到裡面的 DatabaseServiceProvider

/**  
 * Register the service provider. * * @return void  
 */public function register()  
{  
    Model::clearBootedModels();  
  
    $this->registerConnectionServices();  
    $this->registerFakerGenerator();  
    $this->registerQueueableEntityResolver();  
}

這裡面會透過 registerConnectionServices() 建立連線相關的物件

protected function registerConnectionServices()
{
	// The connection factory is used to create the actual connection instances on
	// the database. We will inject the factory into the manager so that it may
	// make the connections while they are actually needed and not of before.
	$this->app->singleton('db.factory', function ($app) {
		return new ConnectionFactory($app);
	});

	// The database manager is used to resolve various connections, since multiple
	// connections might be managed. It also implements the connection resolver
	// interface which may be used by other components requiring connections.
	$this->app->singleton('db', function ($app) {
		return new DatabaseManager($app, $app['db.factory']);
	});

	$this->app->bind('db.connection', function ($app) {
		return $app['db']->connection();
	});

	$this->app->bind('db.schema', function ($app) {
		return $app['db']->connection()->getSchemaBuilder();
	});

	$this->app->singleton('db.transactions', function ($app) {
		return new DatabaseTransactionsManager;
	});
}

這邊的 ConnectionFactory 作用如名稱所述,用來建立各種 Connection 物件。

/**
 * Establish a PDO connection based on the configuration.
 *
 * @param  array  $config
 * @param  string|null  $name
 * @return \Illuminate\Database\Connection
 */
public function make(array $config, $name = null)
{
	$config = $this->parseConfig($config, $name);

	if (isset($config['read'])) {
		return $this->createReadWriteConnection($config);
	}

	return $this->createSingleConnection($config);
}

這邊我們選 createSingleConnection() 往下研究

/**
 * Create a single database connection instance.
 *
 * @param  array  $config
 * @return \Illuminate\Database\Connection
 */
protected function createSingleConnection(array $config)
{
	$pdo = $this->createPdoResolver($config);

	return $this->createConnection(
		$config['driver'], $pdo, $config['database'], $config['prefix'], $config
	);
}

這邊我們看到 createSingleConnection() 只做兩件事情:建立 PDO Resolver 和建立連線。

首先我們看 createPdoResolver()

/**
 * Create a new Closure that resolves to a PDO instance.
 *
 * @param  array  $config
 * @return \Closure
 */
protected function createPdoResolver(array $config)
{
	return array_key_exists('host', $config)
						? $this->createPdoResolverWithHosts($config)
						: $this->createPdoResolverWithoutHosts($config);
}

連線 MySQL 會有 Host,所以我們追 createPdoResolverWithHosts

/**  
 * Create a new Closure that resolves to a PDO instance with a specific host or an array of hosts. * * @param  array  $config  
 * @return \Closure  
 * * @throws \PDOException  
 */protected function createPdoResolverWithHosts(array $config)  
{  
    return function () use ($config) {  
        foreach (Arr::shuffle($this->parseHosts($config)) as $host) {  
            $config['host'] = $host;  
  
            try {  
                return $this->createConnector($config)->connect($config);  
            } catch (PDOException $e) {  
                continue;  
            }  
        }  
  
        if (isset($e)) {  
            throw $e;  
        }  
    };  
}

找到對應 $host 之後,我們進入到 $this->createConnector()

/**
 * Create a connector instance based on the configuration.
 *
 * @param  array  $config
 * @return \Illuminate\Database\Connectors\ConnectorInterface
 *
 * @throws \InvalidArgumentException
 */
public function createConnector(array $config)
{
	if (! isset($config['driver'])) {
		throw new InvalidArgumentException('A driver must be specified.');
	}

	if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
		return $this->container->make($key);
	}

	return match ($config['driver']) {
		'mysql' => new MySqlConnector,
		'mariadb' => new MariaDbConnector,
		'pgsql' => new PostgresConnector,
		'sqlite' => new SQLiteConnector,
		'sqlsrv' => new SqlServerConnector,
		default => throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]."),
	};
}

到了這邊我們看到支援的所有 Connector,如前面所說,我們選 MySqlConnector 往下看

/**
 * Establish a database connection.
 *
 * @param  array  $config
 * @return \PDO
 */
public function connect(array $config)
{
	$dsn = $this->getDsn($config);

	$options = $this->getOptions($config);

	// We need to grab the PDO options that should be used while making the brand
	// new connection instance. The PDO options control various aspects of the
	// connection's behavior, and some might be specified by the developers.
	$connection = $this->createConnection($dsn, $config, $options);

	if (! empty($config['database'])) {
		$connection->exec("use `{$config['database']}`;");
	}

	$this->configureConnection($connection, $config);

	return $connection;
}

這邊開始會用到 Connector 內的方法了,接著我們看 Connector::createConnection() 的實做

public function createConnection($dsn, array $config, array $options)
{
	[$username, $password] = [
		$config['username'] ?? null, $config['password'] ?? null,
	];

	try {
		return $this->createPdoConnection(
			$dsn, $username, $password, $options
		);
	} catch (Exception $e) {
		return $this->tryAgainIfCausedByLostConnection(
			$e, $dsn, $username, $password, $options
		);
	}
}

createPdoConnection() 實作則是

protected function createPdoConnection($dsn, $username, $password, $options)
{
	return new PDO($dsn, $username, $password, $options);
}

到這邊,我們總算追到了 Laravel 建立連線的方式:使用 PHP 的 PDO 物件,以及宣告的位置在哪。

今天我們先看到這邊。


上一篇
Day 12:建立資料表的 make:migration 指令
下一篇
Day 14:Query Builder 的 DB::table()
系列文
Laravel 那麼好用還需要自幹框架嗎18
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言